﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Diagnostics;
using E4D.AppFabric.Entities;


namespace MDCAdminTool
{
    public abstract class PowerShellBase<TParent,TChild> : CacheEntityBase , IDisposable , ICacheCommand<TChild>
        where TChild : CacheEntityBase
    {
        #region Fields

        protected  PowerShell powerShell;
        protected  Runspace RunSpace;
        protected  Pipeline PipeLine;
        protected Dictionary<string, object> Params;

        public bool IsRunSpaceOpen { get; set; }
        public new TParent Parent { get; set; }
        #endregion

        protected PowerShellBase()
        {
            IsRunSpaceOpen = false;
            OpenRunSpace();
        }
        ~PowerShellBase()
        {
            Dispose();
        }

        private void OpenRunSpace()
        {
            PSSnapInException snapInException = null;

            try
            {
                powerShell = PowerShell.Create();
                powerShell.AddCommand( "Import-Module");
                powerShell.AddParameter( "Name" , "DistributedCacheAdministration" );
                powerShell.Invoke();


                //powerShell.AddParameter( "Name" , @"C:\Windows\System32\WindowsPowerShell\v1.0\Modules\DistributedCacheAdministration\DistributedCacheAdministration.psd1" );
                //powerShell.AddParameter("Name"  ,@"C:\Windows\System32\WindowsPowerShell\v1.0\Modules\DistributedCacheConfiguration\DistributedCacheConfiguration.psd1");

                //powerShell.Runspace
                //    .RunspaceConfiguration
                //    .AddPSSnapIn("Microsoft.Data.Caching.Commands", out snapInException);

                //powerShell.Runspace.Open();

                //var rsConfig = RunspaceConfiguration.Create();
                //var info     = rsConfig.AddPSSnapIn( "Microsoft.Data.Caching.Commands" , out snapInException );
                

                //RunSpace = RunspaceFactory.CreateRunspace( rsConfig );
                //RunSpace.Open();

                IsRunSpaceOpen = true;
            }
            catch ( Exception ex)
            {
                IsRunSpaceOpen = false;

                if (snapInException != null) 
                    Log( new ErrorMessage { Message = snapInException.Message } );

                Log( new ErrorMessage { Message = ex.Message } );
            }
        }

        protected Collection<PSObject> Invoke( string commandName , bool isAsync = false )
        {
            return Invoke(commandName, new Dictionary<string, object>());
        }
        protected Collection<PSObject> Invoke( string commandName , Dictionary<string , object> parameters, bool isAsync = false )
        {
            Collection<PSObject> Result = null;
            
            AddResultToLog( new PowerShellCommand{ CommandName = commandName, Parameters = parameters} );
            
            Command cmd = null;
            
            try
            {
                cmd = new Command( commandName );

                foreach ( var c in parameters )
                {
                    cmd.Parameters.Add( new CommandParameter( c.Key , c.Value ) );
                }

                if ( !IsRunSpaceOpen )//if( RunSpace == null )
                {
                    OpenRunSpace();       
                }

                //powerShell.Commands.AddCommand( cmd );
                //Result = powerShell.Invoke();

                PipeLine = powerShell.Runspace.CreatePipeline();
                PipeLine.Commands.Add( cmd );

                if( isAsync )
                {
                    PipeLine.InvokeAsync();
                }
                else
                {
                    Result = PipeLine.Invoke();
                    InsertResultToOutput(Result);
                }
            }
            catch ( Exception exception)
            {
                Debug.WriteLine( exception.Message );
                if ( Output != null )
                {
                    AddResultToLog( new ErrorMessage { Message = exception.Message } );
                }
            }

            return Result;
        }
        
        void InsertResultToOutput( IEnumerable<PSObject> result )
        {
            foreach (var obj in result)
            {
                if ( Output != null )
                {
                    if ( obj.BaseObject.GetType().Name == "HostCacheStats" )
                    {
                        AddResultToLog( obj.ConvertPSObjectToHostCacheStats() );
                    }
                    else if ( obj.BaseObject.GetType().Name == "NamedCacheStats" )
                    {
                        AddResultToLog( obj.ConvertPSObjectToNamedCacheStats() );
                    }
                    else if ( obj.BaseObject.GetType().Name == "CacheConfig" )
                    {
                        AddResultToLog( obj.ConvertPSObjectToCacheConfig() );
                    }
                    else if ( obj.BaseObject.GetType().Name == "HostConfig" )
                    {
                        AddResultToLog( obj.ConvertPSObjectToHostConfig() );
                    }
                    else if ( obj.BaseObject.ToString().Contains("ErrorCode") )
                    {
                        AddResultToLog( new ErrorMessage{ Message =  obj.BaseObject.ToString() } );
                    }
                    else
                    {
                        AddResultToLog( obj.BaseObject );
                    }
                }
            }
        }
        protected void AddResultToLog( object obj )
        {
            if( Output == null ) return;

            Log( obj );
        }

        #region IDisposable Members
        public void Dispose()
        {
            if (RunSpace != null) RunSpace.CloseAsync();
            GC.SuppressFinalize(this);
        }

        #endregion

        #region ICacheCommand<TChild> Members
        public virtual void Add( TChild item )
        {
            Items.Add( item );
        }
        public virtual void Remove( TChild item )
        {
            Items.Remove(item);
        }
        public Collection<TChild> Items { get; set; }
        #endregion
    }
}
